// 智能查找集数并命名
import fs from 'node:fs/promises';
import path from 'node:path';
import { clipFileName, extractNumbers, createRegExpFromArray, getUniquePairs, formatNumberWithDigits, parseCustomArray } from '../common.js';
import { writeAiFindLog, addAiFindLog } from './jsonData.js';

const args = process.argv.slice(2);
const args_1 = args[0];

const arg2 = parseCustomArray(String.raw`${args[1]}`);
console.log(arg2);
// 配置：要排除的文件后缀列表----你可以根据需要修改这个数组
const excludedExtensions = ['.tmp', '.log', '.swp', '.DS_Store', '.torrent', '.md5'];

// 数字位数，默认是3
const digits = arg2 ? arg2.num : 3;

// 前缀，默认为空
const prefix = arg2 ? arg2.str1 : '';

// 后缀，默认为空
const suffix = arg2 ? arg2.str2 : '';

// 输入路径
const inputDir = String.raw`${args_1}`;

// 清空记录文件（或自己手动清除aiFindLog.json）
// writeAiFindLog({});

// 输出两个字符串的重叠部分，条件是至少含有一个数字，总字符数至少是1，并且至少有一个非数字
const getCommonSubstringsWithCondition = (str1, str2) => {
    const dp = Array.from({ length: str1.length + 1 }, () => Array(str2.length + 1).fill(0));
    const substrings = [];

    // 遍历动态规划表，收集所有符合条件的子串
    for (let i = 1; i <= str1.length; i++) {
        for (let j = 1; j <= str2.length; j++) {
            if (str1[i - 1] === str2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
                const length = dp[i][j];
                if (length >= 3) {
                    const start1 = i - length;
                    const end1 = i - 1;
                    const start2 = j - length;
                    const end2 = j - 1;
                    const substring = str1.substring(start1, end1 + 1);

                    // 新增条件：至少包含一个非数字字符、数字必须在非数字中间
                    if (substring.length >= 3 && /\d/.test(substring) && /\D/.test(substring) && /^\D+\d+\D+$/.test(substring)) {
                        substrings.push({
                            value: substring,
                            start1,
                            end1,
                            start2,
                            end2
                        });
                    }
                }
            }
        }
    }

    // 去重：防止重复的子串
    const seen = new Set();
    const uniqueSubstrings = [];
    for (const sub of substrings) {
        const key = `${sub.value}-${sub.start1}-${sub.end1}-${sub.start2}-${sub.end2}`;
        if (!seen.has(key)) {
            seen.add(key);
            uniqueSubstrings.push(sub);
        }
    }

    // 按子串长度降序排序，确保优先选择更长的
    uniqueSubstrings.sort((a, b) => b.value.length - a.value.length);

    // 贪心算法：选择不重叠的子串（在两个字符串中均不重叠）
    const result = [];
    let lastEnd1 = -1;
    let lastEnd2 = -1;
    for (const sub of uniqueSubstrings) {
        if (sub.start1 > lastEnd1 && sub.start2 > lastEnd2) {
            result.push(sub.value);
            lastEnd1 = sub.end1;
            lastEnd2 = sub.end2;
        }
    }

    return result;
};

const normalizeStringByCommonSubstring = (arr, startAttrName, EndAttrName) => {
    const towAllArrays = getUniquePairs(arr);
    const sameSArr = new Set();
    towAllArrays.forEach(([s, e]) => {
        getCommonSubstringsWithCondition(s[startAttrName], e[startAttrName]).forEach(str => sameSArr.add(str));
    });
    const exp = createRegExpFromArray(sameSArr.values());
    // console.log('看看exp', exp);
    arr.forEach(item => {
        item[EndAttrName] = item[startAttrName].replace(exp, '-');
    });

    return arr;
};

// 输入{ 'a':'1' }
const findMultipleContinuousSequences = obj => {
    const keys = Object.keys(obj);
    const arrays = keys.map(key => ({
        key,
        array: obj[key].numArr
            .map(num => {
                // 统一转成数字，并过滤非有效数字
                const parsed = parseInt(num, 10);
                return isNaN(parsed) ? null : parsed;
            })
            .filter(num => num !== null && num > 0),
        newName: obj[key].newName,
        fileName: obj[key].fileName,
        typeStr: obj[key].typeStr,
        newCompName: '' // 计算名称，暂时为空
    }));

    // 计算强度为2次(计算2次)足矣,从newName到newCompName，第二次从newCompName到newCompName2
    normalizeStringByCommonSubstring(arrays, 'newName', 'newCompName');
    normalizeStringByCommonSubstring(arrays, 'newCompName', 'newCompName2');

    arrays.forEach(item => {
        item.array = extractNumbers(item.newCompName2).map(num => parseInt(num, 10));
    });

    // console.log('看看arrays', arrays);

    // 阶段：过滤---生成新的处理数组---过滤无效（没有准确数字了）
    const arrays2 = arrays.filter(item => item.array.length >= 1);

    // 把过滤掉的写入log
    addAiFindLog(Object.fromEntries(arrays.filter(item => item.array.length < 1).map(({ fileName }) => [fileName, '无法识别、无需识别、已排除'])));

    // console.log('看看arrays2', arrays2);
    // 收集所有正数
    const allNums = arrays2.flatMap(item => item.array);

    if (allNums.length === 0) return false;

    // 去重排序后尝试作为起始点---按顺序编编排
    const uniqueNums = [...new Set(allNums)].sort((a, b) => a - b);
    const validMaps = [];

    for (let i = 0; i < uniqueNums.length; i++) {
        const start = uniqueNums[i];
        const length = arrays2.length;
        const expectedSequence = Array.from({ length }, (_, idx) => start + idx);

        const resultMap = [];

        for (let j = 0; j < expectedSequence.length; j++) {
            const num = expectedSequence[j];

            for (let k = 0; k < arrays2.length; k++) {
                // 以num为主
                if (arrays2[k].array.includes(num)) {
                    const index = resultMap.findIndex(item => item.num === num);
                    if (index === -1) {
                        resultMap.push({
                            num,
                            objArr: [arrays2[k]]
                        });
                    } else {
                        resultMap[index].objArr.push(arrays2[k]);
                    }
                }
            }

            if (Object.keys(resultMap).length === length) {
                validMaps.push(resultMap);
            }
        }
    }

    // 终极去重
    for (let v = 0; v < validMaps.length; v++) {
        const rm = validMaps[v];
        const oneA = rm.filter(item => item.objArr.length === 1);
        const oneAKeys = oneA.map(item => item.objArr[0].key);
        const towMoreA = rm.filter(item => item.objArr.length > 1);
        towMoreA.forEach(item => {
            const objArr = item.objArr;
            oneAKeys.forEach(key => {
                const index = objArr.findIndex(itemx => itemx.key === key);
                if (index > -1) {
                    objArr.splice(index, 1);
                }
            });
            item.objArr = objArr;
        });
    }

    // 输出逻辑
    if (validMaps.length >= 2) {
        return [['无法识别', `${inputDir}似乎有多条序列`]];
    } else if (validMaps.length === 1) {
        // 过滤不确定
        const validArr = validMaps[0].map(item => {
            const objArr = item.objArr;
            const Enum = formatNumberWithDigits(item.num, digits);
            const len = objArr.length;
            return len === 1 ? [objArr[0].fileName, `${prefix}E${Enum}${suffix}.${objArr[0].typeStr}`] : null;
        });

        addAiFindLog(Object.fromEntries(validArr.filter(item => item[1] === null).map(item => [item[0], '似乎适应多个编号,无法识别'])));

        return validArr.filter(item => item[1] !== null);
    } else {
        return [['无法识别', `${inputDir}找不到序列`]];
    }
};

// 默认为1,就是保留文件末尾多少个“."，最小值是0,如果是0,则把文件格式一起给改没了，所以请慎重。非必要不碰
const typeNum = 1;
/**
 * 获取指定目录下的所有非隐藏、非文件夹的文件名列表（支持 Windows 隐藏属性 + 排除指定后缀）
 * @param {string} dirPath - 要读取的文件夹路径
 * @returns {Promise<string[]>} - 文件名数组
 */
const getNonHiddenFiles = async dirPath => {
    try {
        const files = await fs.readdir(dirPath, { withFileTypes: true });
        const nonHiddenFiles = [];

        for (const file of files) {
            const filePath = path.join(dirPath, file.name);

            // 只处理文件，跳过文件夹
            if (!file.isDirectory()) {
                // 检查后缀是否被排除
                const ext = path.extname(file.name).toLowerCase();
                if (excludedExtensions.includes(ext)) {
                    console.log(`跳过文件（后缀匹配、可能还是隐藏文件）: ${file.name}`);
                    continue;
                }

                try {
                    const stats = await fs.stat(filePath);

                    // Windows 上隐藏文件的判定方式
                    if (process.platform === 'win32') {
                        // eslint-disable-next-line no-bitwise
                        const isHidden = stats.isFile() && stats.attributes & 0x2; // FILE_ATTRIBUTE_HIDDEN = 0x2
                        if (isHidden) {
                            console.log(`跳过文件（隐藏属性）: ${file.name}`);
                            continue;
                        }
                    } else {
                        // 其他系统继续使用文件名前缀判断隐藏文件
                        if (file.name.startsWith('.')) {
                            console.log(`跳过文件（隐藏文件名）: ${file.name}`);
                            continue;
                        }
                    }

                    // 所有条件通过，加入结果数组
                    nonHiddenFiles.push(file.name);
                } catch (err) {
                    console.error(`读取文件状态出错: ${filePath}`, err.message);
                }
            }
        }

        return nonHiddenFiles;
    } catch (error) {
        console.error('读取文件夹出错:', error.message);
        throw error;
    }
};

getNonHiddenFiles(inputDir)
    .then(files => {
        const fileNames = files.map(fileName => {
            const { newName, typeStr } = clipFileName(fileName, typeNum);
            const numArr = extractNumbers(newName);
            return { fileName, newName, typeStr, numArr };
        });
        const obj = fileNames.reduce((acc, { fileName, newName, typeStr, numArr }) => {
            acc[fileName] = {
                numArr: numArr.map(num => parseInt(num, 10)),
                newName,
                fileName,
                typeStr
            };
            return acc;
        }, {});

        addAiFindLog(Object.fromEntries(findMultipleContinuousSequences(obj)));
        console.log('改名已记录日志到aiFindLog.json');
    })
    .catch(err => console.error('错误:', err));
